package fr.ysa.persistence;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import fr.ysa.persistence.requetes.ConfigRequetes;
import fr.ysa.persistence.requetes.Trie;
import fr.ysa.persistence.requetes.filtres.Filtre;
import fr.ysa.persistence.requetes.filtres.utils.FiltreUtil;

@Repository("jpaDAO")
@Transactional
public class ImplJpaDao<T extends Entity> implements IDao<T> {

	private Class<T> clazz;

	private static EntityManager em;

	@Autowired
	FiltreUtil filtreUtil;

	@PersistenceContext
	public void setEntityManager(EntityManager em) {
		ImplJpaDao.em = em;
	}

	public static EntityManager getEm() {
		return em;
	}

	public void setClazz(final Class<T> clazzToSet) {
		this.clazz = clazzToSet;

	}

	@Override
	public T sauvegarder(T obj) throws DaoException {
		try {
			em.persist(obj);
			return obj;
		} catch (Exception e) {
			throw new DaoException(e);
		}
	}

	@Override
	@Transactional(propagation=Propagation.REQUIRES_NEW)
	public Collection<T> sauvegarder(Collection<T> entities) throws DaoException {
		try {
			for (T entity : entities) {
				sauvegarder(entity);
			}
			return entities;
		} catch (Exception e) {
			throw new DaoException(e);
		}
	}

	@Override
	public T modifier(T obj) throws DaoException {
		try {
			return em.merge(obj);
		} catch (Exception e) {
			throw new DaoException(e);
		}
	}

	@Override
	public void supprimer(T obj) throws DaoException {
		try {
			obj = em.merge(obj);
			em.remove(obj);
		} catch (Exception e) {
			throw new DaoException(e);
		}
	}

	@Override
	public void supprimerParId(Serializable id) throws DaoException {
		try {
			final T entity = trouver(id);
			supprimer(entity);
		} catch (Exception e) {
			throw new DaoException(e);
		}
	}

	@Override
	public void supprimer(Collection<T> entities) throws DaoException {
		for (T t : entities) {
			supprimer(t);
		}

	}

	@Override
	public Integer compter(ConfigRequetes config) throws DaoException {
		String queryString = "select count(c) from " + clazz.getName() + " c ";

		queryString = addFiltre(queryString, config);

		queryString = addGroup(queryString, config);

		Query query = getEm().createQuery(queryString);
		Long count = (Long) query.getSingleResult();
		return count.intValue();
	}

	@Override
	public T trouver(Serializable key) throws DaoException {
		try {
			return em.find(clazz, key);
		} catch (Exception e) {
			throw new DaoException(e);
		}

	}

	@SuppressWarnings("unchecked")
	@Override
	public final List<T> trouver(ConfigRequetes config) throws DaoException {
		try {
			String queryString = "from " + clazz.getName();

			queryString = addFiltre(queryString, config);

			queryString = addGroup(queryString, config);

			queryString = addTrie(queryString, config);
			
			// creation de la requete

			Query query = getEm().createQuery(queryString);

			// recupere a partir de lelement:

			query.setFirstResult(config.getDebut());

			// combien delement recuperer

			if (config.getNombre() > 0) {
				query.setMaxResults(config.getNombre());
			}

			// execution de la requete

			List<T> resultQuery = query.getResultList();
			return resultQuery;
		} catch (Exception e) {
			throw new DaoException(e);
		}

	}

	private String addFiltre(String requete, ConfigRequetes config) {
		String queryStringResult = requete;

		// filtres de valeurs sur les colonnes
		String valueFiltres = "";

		if (config.getFiltres() != null) {
			int ind = 0;
			for (Filtre filtre : config.getFiltres()) {
				valueFiltres += filtreUtil.getValue(filtre);
				ind++;
				if (ind < config.getFiltres().size()) {
					valueFiltres += " AND ";
				}
			}
		}

		if (!"".equals(valueFiltres)) {
			queryStringResult += " where " + valueFiltres;
		}

		return queryStringResult;
	}

	private String addGroup(String requete, ConfigRequetes config) {
		String queryStringResult = requete;
		String queryGroupby = " Group by ";

		// regroupement par valeurs (GROUP BY)

		if (config.getRegroupement().size() > 0) {
			int ind = 0;
			for (String group : config.getRegroupement()) {
				queryGroupby += group;
				ind++;
				if (ind < config.getRegroupement().size()) {
					queryGroupby += ", ";
				}
			}
			queryStringResult += queryGroupby;
		}

		return queryStringResult;
	}

	private String addTrie(String requete, ConfigRequetes config) {
		String queryStringResult = requete;
		String queryOrders = " Order by ";

		// ordonnés selon les valeurs (ASC, DESC)

		if (config.getTries().size() > 0) {
			int ind = 0;
			for (Trie trie : config.getTries()) {
				queryOrders += trie.getChamp() + " " + trie.getOrdre();
				ind++;
				if (ind < config.getTries().size()) {
					queryOrders += ", ";
				}
			}
			queryStringResult += queryOrders;
		}

		return queryStringResult;
	}

}
